package name.pehl.totoe.xml.client.internal; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JavaScriptException; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.ScriptInjector; import com.google.gwt.resources.client.ClientBundle; import com.google.gwt.resources.client.TextResource; import name.pehl.totoe.xml.client.Document; import name.pehl.totoe.xml.client.XmlParseException; /** * @author $Author$ * @version $Date$ $Revision: 629 * $ */ public class XmlParserImpl { // ------------------------------------------------------- script resources interface ResourceBundle extends ClientBundle { ResourceBundle INSTANCE = GWT.create(ResourceBundle.class); @Source("sarissa.js") TextResource sarissaJs(); @Source("sarissa_ieemu_xpath.js") TextResource sarissaXPathJs(); } // ------------------------------------------------------------------- JSNI private static final JavaScriptObject nativeParser = XmlParserImpl.initialize(); private static JavaScriptObject initialize() { injectScript(); setupSarissaPrototypes(); return initializeDOMParser(); } private static void injectScript() { if (!isScriptInjected()) { ScriptInjector.fromString(ResourceBundle.INSTANCE.sarissaJs().getText()) .setWindow(ScriptInjector.TOP_WINDOW).inject(); ScriptInjector.fromString(ResourceBundle.INSTANCE.sarissaXPathJs().getText()) .setWindow(ScriptInjector.TOP_WINDOW).inject(); } } private static final native boolean isScriptInjected() /*-{ if (!(typeof $wnd.Sarissa === "undefined") && !(null === $wnd.Sarissa)) { return true; } return false; }-*/; /** * Workaround for a bug where the Element javascript object is created in * different scopes. Many thanks to Greg Hengeli! See <a * href="http://code.google.com/p/totoe/issues/detail?id=3">http://code * .google.com/p/totoe/issues/detail?id=3</a> for more details. */ private static native void setupSarissaPrototypes() /*-{ if ($wnd.Sarissa._SARISSA_HAS_DOM_FEATURE && document.implementation.hasFeature("XPath", "3.0")) { XMLDocument.prototype._sarissa_useCustomResolver = false; XMLDocument.prototype._sarissa_xpathNamespaces = []; XMLDocument.prototype.selectNodes = function(sExpr, contextNode, returnSingle) { var nsDoc = this; var nsresolver; if (this._sarissa_useCustomResolver) { nsresolver = function(prefix) { var s = nsDoc._sarissa_xpathNamespaces[prefix]; if (s) { return s; } else { throw "No namespace URI found for prefix: '" + prefix + "'"; } }; } else { nsresolver = this.createNSResolver(this.documentElement); } var result = null; if (!returnSingle) { var oResult = this.evaluate(sExpr, (contextNode ? contextNode : this), nsresolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); var nodeList = new SarissaNodeList(oResult.snapshotLength); nodeList.expr = sExpr; for ( var i = 0; i < nodeList.length; i++) { nodeList[i] = oResult.snapshotItem(i); } result = nodeList; } else { result = this.evaluate(sExpr, (contextNode ? contextNode : this), nsresolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; } return result; }; Element.prototype.selectNodes = function(sExpr) { var doc = this.ownerDocument; if (doc.selectNodes) { return doc.selectNodes(sExpr, this); } else { throw "Method selectNodes is only supported by XML Elements"; } }; XMLDocument.prototype.selectSingleNode = function(sExpr, contextNode) { var ctx = contextNode ? contextNode : null; return this.selectNodes(sExpr, ctx, true); }; Element.prototype.selectSingleNode = function(sExpr) { var doc = this.ownerDocument; if (doc.selectSingleNode) { return doc.selectSingleNode(sExpr, this); } else { throw "Method selectNodes is only supported by XML Elements"; } }; } }-*/; private static native JavaScriptObject initializeDOMParser() /*-{ return new $wnd.DOMParser(); }-*/; // ---------------------------------------------------------- parse methods public Document parse(String xml, String namespaces) { if (xml == null || xml.trim().length() == 0) { return null; } try { JavaScriptObject documentJso = parseImpl(xml, namespaces); return NodeFactory.create(documentJso); } catch (JavaScriptException e) { throw new XmlParseException(e.getMessage(), e); } } private native JavaScriptObject parseImpl(String xml, String namespaces) /*-{ var domDoc = @name.pehl.totoe.xml.client.internal.XmlParserImpl::nativeParser .parseFromString(xml, "text/xml"); var error = $wnd.Sarissa.getParseErrorText(domDoc); if (error != $wnd.Sarissa.PARSED_OK) { if (error == $wnd.Sarissa.PARSED_EMPTY) { return null; } else if (error == $wnd.Sarissa.PARSED_UNKNOWN_ERROR) { throw (new Error("Unkown error parsing xml file.")); } else { throw (new Error(error)); } return null; } if (namespaces != null) { try { $wnd.Sarissa.setXpathNamespaces(domDoc, namespaces); } catch (e) { throw (new Error(e)); } } return domDoc; }-*/; }